﻿using AutoMapper;
using FlyingEye.Application.Exceptions;
using FlyingEye.Application.NetServices;
using FlyingEye.Exceptions;
using FlyingEye.HttpServices;
using FlyingEye.NetServices;
using FlyingEyeClient.ApplicationServices.NetServices;
using FlyingEyeClient.Models;
using HslCommunication.Profinet.Siemens;
using Microsoft.Extensions.Options;
using System.Collections.ObjectModel;

namespace FlyingEye.DeviceClients
{
    public class DeviceConnectedEventArgs : EventArgs
    {
        public ReadOnlyCollection<DeviceConnectException>? ConnectExceptions { get; }

        public DeviceConnectedEventArgs(ReadOnlyCollection<DeviceConnectException>? exceptions)
        {
            ConnectExceptions = exceptions;
        }
    }

    public class DeviceManager
    {
        private readonly HttpClientService _httpClientService;

        private readonly ApplicationOptions _applicationOptions;

        private readonly List<IDevice> _devices;

        public event EventHandler<DeviceConnectedEventArgs>? DeviceConnected;

        private readonly IMapper _mapper;

        public DeviceManager(IOptions<ApplicationOptions> options, HttpClientService httpClientService, IMapper mapper)
        {
            _applicationOptions = options.Value;
            _httpClientService = httpClientService;
            _devices = new List<IDevice>();
            _mapper = mapper;
        }

        public bool TryGetDevice(string name, out IDevice outDevice)
        {
            var device = _devices.FirstOrDefault(item => item.DeviceName == name);

            if (device == null)
            {
                outDevice = null!;
                return false;
            }

            outDevice = device;
            return true;
        }

        private IDevice CreateDeviceNetService(DeviceConfigurationResponse deviceConfig)
        {
            return deviceConfig.DeviceType switch
            {
                DeviceTypes.SiemensS1500 => new S7NetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port, SiemensPLCS.S1500, deviceConfig.Slot ?? 0, deviceConfig.Rack ?? 0),
                DeviceTypes.SiemensS1200 => new S7NetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port, SiemensPLCS.S1200, deviceConfig.Slot ?? 0, deviceConfig.Rack ?? 0),
                DeviceTypes.SiemensS200 => new S7NetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port, SiemensPLCS.S200, deviceConfig.Slot ?? 0, deviceConfig.Rack ?? 0),
                DeviceTypes.SiemensS300 => new S7NetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port, SiemensPLCS.S300, deviceConfig.Slot ?? 0, deviceConfig.Rack ?? 0),
                DeviceTypes.SiemensS400 => new S7NetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port, SiemensPLCS.S400, deviceConfig.Slot ?? 0, deviceConfig.Rack ?? 0),
                DeviceTypes.OmronFins => new OmronFinsDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port),
                DeviceTypes.OmronCIP => new OmronCipDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port),
                DeviceTypes.Mitsubishi => new MelsecMcNetDevice(deviceConfig.DeviceName, deviceConfig.IPAddress, deviceConfig.Port),
                _ => throw new InvalidOperationException("不支持的设备类型。")
            };
        }

        public async Task ConnectAsync(string resourceId)
        {
            var configurations = await _httpClientService.GetAsync<List<DeviceConfigurationResponse>>(
                $"{_applicationOptions.HttpBaseUrl}/api/device-app-service/v1.0/device-clients/{resourceId}/configurations");

            var exceptions = new List<DeviceConnectException>();

            foreach (var configuration in configurations)
            {
                var device = CreateDeviceNetService(configuration);

                try
                {
                    device.Connect();
                    var configurationArg = _mapper.Map<DeviceConfigurationResponse, DeviceConfiguration>(configuration);
                   
                    _devices.Add(device);
                }
                catch (DeviceConnectException ex)
                {
                    exceptions.Add(ex);
                }
                catch (Exception)
                {
                    // 如果连接出现问题，关闭现有的所有连接
                    await CloseAllAsync();
                    throw;
                }
            }

            if (exceptions.Any())
            {
                DeviceConnected?.Invoke(
                    this,
                    new DeviceConnectedEventArgs(new ReadOnlyCollection<DeviceConnectException>(exceptions)));
                throw new DeviceConnectAggregateException(new ReadOnlyCollection<DeviceConnectException>(exceptions));
            }
            else
            {
                DeviceConnected?.Invoke(
                  this,
                  new DeviceConnectedEventArgs(null));
            }
        }

        /// <summary>
        /// 关闭所有连接
        /// </summary>
        public Task CloseAllAsync()
        {
            foreach (var device in _devices)
            {
                try
                {
                    device.Close();
                }
                catch (Exception) { }
            }

            _devices.Clear();

            return Task.CompletedTask;
        }
    }
}
